using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using LambdaBuilder.Resolver;
using Catastrophe.Common.AttributeEx;

namespace HslCommunicationEx.VarMonitor
{
	public class VarMonitorCheck<T> where T : class, new()
	{
		private T _entity = new T();

		private readonly List<PropertyInfo> _listExceptVarMonitor;

		private readonly Dictionary<string, object> _dicVar = new Dictionary<string, object>();

		public T Entity { get; set; } = new T();


		public VarMonitorCheck()
		{
			this._listExceptVarMonitor = (from x in typeof(T).GetProperties()
				where !(Attribute.GetCustomAttribute(x, typeof(VarMonitorAttribute)) is VarMonitorAttribute)
				select x).Where(delegate(PropertyInfo x)
			{
				ComDataAttribute comDataAttribute = Attribute.GetCustomAttribute(x, typeof(ComDataAttribute)) as ComDataAttribute;
				return comDataAttribute != null && comDataAttribute.OprType == OprTypeEnum.Read;
			}).ToList();
		}

		public void DataSourceRefresh(T entity)
		{
			this._entity = entity;
			foreach (PropertyInfo item in this._listExceptVarMonitor)
			{
				object value = item.GetValue(entity);
				item.SetValue(this.Entity, value);
			}
		}

		public void VarMonitorMulti(int mode = 1, int monitorInterval = 200, int monitorTime = 5000)
		{
			var source = (from x in typeof(T).GetProperties()
				select new
				{
					Property = x,
					CustomAb = (Attribute.GetCustomAttribute(x, typeof(VarMonitorAttribute)) as VarMonitorAttribute)
				} into x
				where x.CustomAb != null
				select x).ToList();
			Dictionary<string, object> dictionary = null;
			switch (mode)
			{
			case 1:
				dictionary = this.VarStateCheckMulti2(source.Select(x => x.Property.Name).ToList(), monitorInterval, monitorTime);
				break;
			case 2:
				dictionary = this.VarStateCheckMulti(source.Select(x => x.Property.Name).ToList()).Result;
				break;
			}
			foreach (string key in dictionary.Keys)
			{
				PropertyInfo property = typeof(T).GetProperty(key);
				if (property != null)
				{
					property.SetValue(this.Entity, dictionary[key]);
				}
			}
		}

		public object GetVarValue(Expression<Func<T, object>> exp)
		{
			string text = LambdaResolver.GetColumnNames(exp).FirstOrDefault();
			if (text != null && this._dicVar.ContainsKey(text))
			{
				return this._dicVar[text];
			}
			return null;
		}

		public Dictionary<string, object> GetVarValueExact(Expression<Func<T, object>> exp)
		{
			try
			{
				List<string> columnNames = LambdaResolver.GetColumnNames(exp);
				if (columnNames.Count > 0)
				{
					return this.VarStateCheckMulti(columnNames).Result;
				}
				return this.VarStateCheckSingle(columnNames.FirstOrDefault());
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		private async Task<Dictionary<string, object>> VarStateCheckMulti(List<string> nameBuf)
		{
			Dictionary<string, object> dic = new Dictionary<string, object>();
			List<Task<Dictionary<string, object>>> list = new List<Task<Dictionary<string, object>>>();
			foreach (string each in nameBuf)
			{
				Task<Dictionary<string, object>> task2 = Task.Run(() => this.VarStateCheckSingle(each));
				list.Add(task2);
			}
			await Task.WhenAll(list.ToArray());
			foreach (Task<Dictionary<string, object>> task in list)
			{
				foreach (string key in task.Result.Keys)
				{
					if (!dic.ContainsKey(key))
					{
						dic.Add(key, task.Result[key]);
					}
				}
			}
			return dic;
		}

		private Dictionary<string, object> VarStateCheckSingle(string name)
		{
			PropertyInfo property = typeof(T).GetProperty(name);
			if (property == null)
			{
				throw new Exception("目标属性不存在!" + name);
			}
			VarMonitorAttribute varMonitorAttribute = Attribute.GetCustomAttribute(property, typeof(VarMonitorAttribute)) as VarMonitorAttribute;
			if (varMonitorAttribute != null)
			{
				Stopwatch stopwatch = new Stopwatch();
				long num = 0L;
				Dictionary<int, object> dictionary = new Dictionary<int, object>();
				int num2 = 0;
				do
				{
					num2++;
					object value = property.GetValue(this._entity);
					dictionary.Add(num2, value);
					stopwatch.Reset();
					stopwatch.Start();
					Thread.Sleep(varMonitorAttribute.MonitorInterval);
					stopwatch.Stop();
					num += stopwatch.ElapsedMilliseconds;
				}
				while (num < varMonitorAttribute.MonitorTime);
				List<object> list = dictionary.Values.Distinct().ToList();
				if (list.Count == 1)
				{
					return new Dictionary<string, object> { 
					{
						name,
						list.First()
					} };
				}
				return new Dictionary<string, object> { { name, null } };
			}
			throw new Exception("目标属性缺少修饰VarMonitorAttribute");
		}

		private Dictionary<string, object> ReadVarMonitorAll(List<string> nameBuf)
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			foreach (string item in nameBuf)
			{
				PropertyInfo property = typeof(T).GetProperty(item);
				if (property == null)
				{
					throw new Exception("目标属性不存在!" + item);
				}
				VarMonitorAttribute varMonitorAttribute = Attribute.GetCustomAttribute(property, typeof(VarMonitorAttribute)) as VarMonitorAttribute;
				if (varMonitorAttribute != null)
				{
					object value = property.GetValue(this._entity);
					dictionary.Add(item, value);
				}
			}
			return dictionary;
		}

		private Dictionary<string, object> VarStateCheckMulti2(List<string> nameBuf, int monitorInterval = 200, int monitorTime = 5000)
		{
			Stopwatch stopwatch = new Stopwatch();
			long num = 0L;
			List<Dictionary<string, object>> list = new List<Dictionary<string, object>>();
			do
			{
				stopwatch.Reset();
				stopwatch.Start();
				Dictionary<string, object> item = this.ReadVarMonitorAll(nameBuf);
				list.Add(item);
				Thread.Sleep(monitorInterval);
				stopwatch.Stop();
				num += stopwatch.ElapsedMilliseconds;
			}
			while (num < monitorTime);
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			Dictionary<string, List<object>> dictionary2 = new Dictionary<string, List<object>>();
			foreach (Dictionary<string, object> item2 in list)
			{
				foreach (string key in item2.Keys)
				{
					if (!dictionary2.ContainsKey(key))
					{
						dictionary2.Add(key, new List<object>());
					}
					dictionary2[key].Add(item2[key]);
				}
			}
			foreach (string key2 in dictionary2.Keys)
			{
				List<object> list2 = dictionary2[key2].Distinct().ToList();
				if (list2.Count == 1)
				{
					dictionary.Add(key2, dictionary2[key2].First());
				}
				else
				{
					dictionary.Add(key2, null);
				}
			}
			return dictionary;
		}

		public void VarMonitor()
		{
			var list = (from x in typeof(T).GetProperties()
				select new
				{
					Property = x,
					CustomAb = (Attribute.GetCustomAttribute(x, typeof(VarMonitorAttribute)) as VarMonitorAttribute)
				} into x
				where x.CustomAb != null && x.CustomAb.MonitorTime > x.CustomAb.MonitorInterval && x.CustomAb.MonitorInterval > 0
				select x).ToList();
			foreach (var each in list)
			{
				if (!each.Property.PropertyType.IsValueType)
				{
					continue;
				}
				object value = Activator.CreateInstance(each.Property.PropertyType);
				this._dicVar.Add(each.Property.Name, value);
				Thread thread = new Thread((ThreadStart)delegate
				{
					object obj = null;
					Stopwatch stopwatch = new Stopwatch();
					long num = 0L;
					while (true)
					{
						object value2 = each.Property.GetValue(this._entity);
						if (obj == null)
						{
							obj = value2;
						}
						else
						{
							stopwatch.Reset();
							stopwatch.Start();
							Thread.Sleep(each.CustomAb.MonitorInterval);
							if (!value2.Equals(obj))
							{
								obj = null;
							}
							else
							{
								stopwatch.Stop();
								num += stopwatch.ElapsedMilliseconds;
								if (num >= each.CustomAb.MonitorTime)
								{
									obj = null;
									num = 0L;
									this._dicVar[each.Property.Name] = value2;
									each.Property.SetValue(this.Entity, value2);
								}
							}
						}
					}
				});
				thread.IsBackground = true;
				thread.Start();
			}
		}
	}
}
